﻿Imports Chilkat
Imports System.Threading
Imports System.ComponentModel

Module modMain

    Private WithEvents ServerSSLSocket As New Socket
    Private WithEvents ServerSocket As New Socket
    Private CertStore As New CertStore
    Private ServerCert As Cert

    Private WithEvents ServerListener As New BackgroundWorker
    Private WithEvents ServerSSLListener As New BackgroundWorker
    Private ClientHandlers As New List(Of BackgroundWorker)

    Sub Main()

        Console.WriteLine("MinecraftUnlocked-Server Ver " & My.Application.Info.Version.ToString & " by GeorgeBush.")
        Console.WriteLine()

        If Not ServerSSLSocket.UnlockComponent("Socket12345678_329BB185e4mS") Then
            BugOut("Failed to unlock HTTPS Socket Component.")
        End If
        If Not ServerSocket.UnlockComponent("Socket12345678_329BB185e4mS") Then
            BugOut("Failed to unlock HTTP Socket Component.")
        End If

        ' Step 1: Load PFX containing server certificate
        Console.Write("Loading Server Certificate... ")
        If CertStore.LoadPfxFile("MinecraftUnlocked.pfx", "minecraft") Then
            Console.WriteLine("OK")
        Else
            BugOut("Unable to load the Server SSL Certificate from MinecraftUnlocked.pfx.")
        End If

        ' Step 2: Find the certificate
        ServerCert = CertStore.FindCertBySubjectCN("*.minecraft.net")
        If ServerCert Is Nothing Then
            BugOut("Unable to find required Server SSL Certificate in loaded PFX.")
        End If

        ' Step 3: Use the loaded certificate
        Console.Write("Setting Server SSL Certificate... ")
        If ServerSSLSocket.InitSslServer(ServerCert) Then
            Console.WriteLine("OK")
        Else
            BugOut("Unable to set Server SSL Certificate.")
        End If

        ' Step 4: Listen for incoming HTTPS connections
        Console.Write("Listening on port 443... ")
        If ServerSSLSocket.BindAndListen(443, 10) Then
            Console.WriteLine("OK")
        Else
            BugOut("Failed to bind to port 443.")
        End If

        ' Step 4: Listen for incoming HTTP connections
        Console.Write("Listening on port 80... ")
        If ServerSocket.BindAndListen(80, 10) Then
            Console.WriteLine("OK")
        Else
            BugOut("Failed to bind to port 80.")
        End If

        ' Step 5: Use a BackgroundWorker to accept any incoming connections
        ServerSSLSocket.MaxReadIdleMs = 10000
        ServerSSLSocket.MaxSendIdleMs = 10000
        AddHandler ServerSSLListener.DoWork, AddressOf ServerListener_DoWork
        ServerSSLListener.RunWorkerAsync(ServerSSLSocket)

        ServerSocket.MaxReadIdleMs = 10000
        ServerSocket.MaxSendIdleMs = 10000
        AddHandler ServerListener.DoWork, AddressOf ServerListener_DoWork
        ServerListener.RunWorkerAsync(ServerSocket)

        Console.ReadLine()

    End Sub

    Private Sub BugOut(Message As String)
        Console.Write(Message & vbCrLf & vbCrLf & "Press any key to exit... ")
        Console.ReadKey()
        End
    End Sub

    Private Sub ServerListener_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)

        Dim sSocket As Socket = CType(e.Argument, Socket)
        Console.WriteLine("HTTP Controller Active on port " & sSocket.LocalPort.ToString)

        Do

            Dim ClientSocket As Socket = Nothing
            ClientSocket = sSocket.AcceptNextConnection(20000)

            If Not ClientSocket Is Nothing Then
                Dim TWorker As New BackgroundWorker
                AddHandler TWorker.DoWork, AddressOf ClientWorker_DoWork
                SyncLock ClientHandlers
                    ClientHandlers.Add(TWorker)
                End SyncLock
                TWorker.RunWorkerAsync(ClientSocket)
            End If

        Loop

    End Sub

    Private Sub ClientWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)

        Dim ThisClient As Socket = CType(e.Argument, Socket)

        Debug.WriteLine("New Client " & ThisClient.ClientIpAddress)

        Do

            If ThisClient.IsConnected = False Then Exit Do

            If ThisClient.PollDataAvailable Then

                Dim Request As String = ThisClient.ReceiveString
                Debug.Print(Request)
                Dim ThisRequest As New HTTPRequest(Request, ThisClient)

                ' Login request
                If UCase(ThisRequest.Method) = "POST" Or UCase(ThisRequest.Method) = "GET" AndAlso LCase(ThisRequest.URI) = "login.minecraft.net/" Then

                    Dim LoginResponse As New Text.StringBuilder
                    LoginResponse.AppendLine("HTTP/1.1 200 OK")
                    LoginResponse.AppendLine("Content-Type: text/plain; charset=UTF-8")
                    LoginResponse.AppendLine("Server: MinecraftUnlocked")
                    LoginResponse.AppendLine("Connection: close" & vbCrLf)

                    With ThisRequest.FormValues
                        If Not .ContainsKey("user") Or Not .ContainsKey("password") Or Not .ContainsKey("version") Then
                            If Not .Count = 3 Then
                                LoginResponse.AppendLine("Bad request" & vbCrLf)
                                ThisClient.SendString(LoginResponse.ToString)
                                ThisClient.Close(5000)
                                Exit Do
                            End If
                        End If
                    End With

                    LoginResponse.AppendLine(GetUnixTimestamp(DateTime.UtcNow) & ":deprecated:" & CType(ThisRequest.FormValues("user"), HTTPRequest.HTTPFormValue).Value & "7ae900:dba0c4" & vbCrLf)
                    ThisClient.SendString(LoginResponse.ToString)
                    ThisClient.Close(5000)
                    Exit Do

                End If

                If UCase(ThisRequest.Method) = "GET" AndAlso LCase(ThisRequest.URI) = "session.minecraft.net/game/joinserver.jsp" Then
                    Dim JoinServerResponse As New Text.StringBuilder
                    JoinServerResponse.AppendLine("HTTP/1.1 200 OK")
                    JoinServerResponse.AppendLine("Content-Type: text/plain; charset=UTF-8")
                    JoinServerResponse.AppendLine("Server: MinecraftUnlocked")
                    JoinServerResponse.AppendLine("Connection: close" & vbCrLf)
                    JoinServerResponse.AppendLine("OK" & vbCrLf)
                    ThisClient.SendString(JoinServerResponse.ToString)
                    ThisClient.Close(5000)
                End If

                If UCase(ThisRequest.Method) = "GET" AndAlso LCase(ThisRequest.URI) = "session.minecraft.net/game/checkserver.jsp" Then
                    Dim JoinServerResponse As New Text.StringBuilder
                    JoinServerResponse.AppendLine("HTTP/1.1 200 OK")
                    JoinServerResponse.AppendLine("Content-Type: text/plain; charset=UTF-8")
                    JoinServerResponse.AppendLine("Server: MinecraftUnlocked")
                    JoinServerResponse.AppendLine("Connection: close" & vbCrLf)
                    JoinServerResponse.AppendLine("YES" & vbCrLf)
                    ThisClient.SendString(JoinServerResponse.ToString)
                    ThisClient.Close(5000)
                End If

            End If

        Loop

        Debug.WriteLine("Client Disconnected")


    End Sub

    Private Function GetUnixTimestamp(ByVal currDate As DateTime) As Long
        Dim span As TimeSpan = (currDate - New DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime())
        Return span.TotalSeconds
    End Function

End Module
